package bee2d.core;

/**
 * ...
 * @author wangjian QQ:421924396
 */
import bee2d.display.Animation;
import bee2d.spritesheet.Behavior;
import bee2d.spritesheet.Frame;
import bee2d.spritesheet.Spritesheet;
import nme.display.BitmapData;
import nme.display.Sprite;
import nme.Lib;
import nme.geom.Matrix;
import nme.Vector;

#if (flash||js)
import nme.geom.ColorTransform;
import nme.display.Bitmap;
#else
import nme.display.Tilesheet;		
import nme.display.Graphics;	
#end
import nme.geom.Point;
import flash.geom.Rectangle;
class Rander
{
	private var layer:Sprite;
	private var childs:Array<Animation>;
	
	private var childsIndex:Hash<Array<Int>>;
	
	private var matrix : Matrix ;
	private var angle:Float; 
	private var offx:Float;
	private var offy:Float;
	private var dis:Float;
	#if (flash || js)
	private var buffer:BitmapData;
	private var point:Point;
	private var bitmap:Bitmap;
	private var colorTransform : ColorTransform ;
	private var clearRect:Rectangle;
	#else
	private var tilesheet :Tilesheet;
	private var tileDatas:Array<Float>;
	#end
	private var frame:Frame;
	private var behavior:Behavior;
	private var renderRect:Rectangle;
	private var curInteratorInx:Int = -1;
	private var curInteratorChild:Animation; //当前迭代元素
	private var swapIndexs:Array<Array<Int>>;
	public var blitMode(get_blitMode,set_blitMode):Bool; //是否批量渲染
	private var _blitMode:Bool;
	private var frameBitmapData:BitmapData;
	public function new(_layer:Sprite,_blitMode:Bool=true,_renderRect:Rectangle=null) 
	{
		this._blitMode = _blitMode;
		childs = new Array<Animation>();
		childsIndex = new Hash<Array<Int>>();
		swapIndexs = new Array();
		if (_layer != null) layer = _layer;
		if (_renderRect == null) {
			renderRect=new Rectangle(0, 0, Lib.current.stage.stageWidth, Lib.current.stage.stageHeight);
		}else {
			renderRect = _renderRect;
		}
		initRanderBuffer();
	}
	
	private function set_blitMode(v:Bool):Bool 
	{
		if (_blitMode == v) return v;
		_blitMode = v;
		for (child in childs) {
			untyped child.updateRanderMode(v);
		}
		initRanderBuffer();
		return v;
	}
	
	private function get_blitMode():Bool 
	{
		return _blitMode;
	}
	
	private function initRanderBuffer():Void {
		if (_blitMode) {
			#if (flash||js)
			buffer = new BitmapData(Std.int(renderRect.width), Std.int(renderRect.height), true, 0);
			if (bitmap==null) {
				bitmap = new Bitmap(buffer);
				bitmap.smoothing = true;
				bitmap.x = renderRect.x;
				bitmap.y = renderRect.y;
			}else {
				bitmap.bitmapData = buffer;
			}
			if(colorTransform==null)colorTransform = new ColorTransform();
			if(point==null)point = new Point();
			clearRect = new Rectangle(0, 0, renderRect.width, renderRect.height);
			layer.addChild(bitmap);
			#end
			if(matrix==null)matrix = new Matrix();
		}else {
			#if (flash || js)
			buffer = null;
			if (bitmap!=null && bitmap.parent!=null) layer.removeChild(bitmap);
			bitmap = null;
			colorTransform = null;
			point = null;
			clearRect = null;
			#elseif (cpp||neko)
			tilesheet = null;
			layer.graphics.clear();
			#end
			matrix = null;
		}
	}
	private function getInteratorNext():Animation {
		if (childs.length > 0) {
			if (curInteratorInx <= -1) {
				curInteratorInx = 0;
			}
			if (curInteratorInx < childs.length) {
				var _curChild:Animation = childs[curInteratorInx];
				if ( _curChild!= curInteratorChild) {
					curInteratorChild = _curChild;
				}else {
					curInteratorChild = null;
					curInteratorInx += 1;
					if(curInteratorInx < childs.length)curInteratorChild = childs[curInteratorInx];
				}
			}
			return curInteratorChild;
		}
		return null;
	}
	public function contains(child:Animation):Bool {
		return child.rander == this;
	}
	public function updateLayer(_layer:Sprite):Void
	{
		if (layer!=null) {
			#if (cpp || neko)
			layer.graphics.clear();
			#else
			buffer.lock();
			buffer.fillRect(clearRect, 0);
			buffer.unlock();
			#end
		}
		if(_layer!=null)layer = _layer;
	}
	
	private function updateChildName(animation:Animation,srcName:String,toName:String):Void {
		var nameInxArr:Array<Int> = childsIndex.get(srcName);
		if ( nameInxArr != null) {
			for (_inx in 0...nameInxArr.length) {
				if ( childs[nameInxArr[_inx]] == animation) {
					nameInxArr.splice(_inx, 1);
					break;
				}
			}
		}
		var inx:Int = this.getChildIndex(animation);
		nameInxArr = childsIndex.get(toName);
		if (nameInxArr!=null) {
			nameInxArr.push(inx);
		}else {
			childsIndex.set(toName, [inx]);
		}
	}
	public function addChild(animation:Animation):Animation
	{
		if (_blitMode) {
			#if (cpp||neko)
			if (tilesheet == null) {
				untyped tilesheet = animation.spritesheet.tileSheet;
			}
			#end
		}		
		var inx:Int;
		var nameInxArr:Array<Int>=childsIndex.get(animation.name);
		if ( nameInxArr!= null) {
			var exites:Bool = false;
			for (_inx in nameInxArr) {
				if (childs[_inx] == animation) {
					exites = true;
					break;
				}
			}
			if (exites == false) {
				inx = childs.push(animation);
				nameInxArr.push(inx-1);
				untyped animation.set_rander(this);
			}
		}else {
			inx = childs.push(animation);
			childsIndex.set(animation.name, [inx-1]);
			untyped animation.set_rander(this);
		}
		return animation;
	}
	public function addChildAt(animation:Animation, index:Int):Animation
	{
		if (index > this.childs.length) return null;
		if (_blitMode==false) {
			layer.addChildAt(animation.sprite, index);
		}
		if (index == this.childs.length) {
			return this.addChild(animation);
		}else {
			this.childs.insert(index, animation);
			if (index <= curInteratorInx) curInteratorInx -= 1;
			return animation;
		}
	}
	public function getChildAt(index:Int):Animation
	{
		if (index >= this.childs.length) return null;
		return childs[index];
	}
	public function removeChild(animation:Animation):Animation
	{
		if (childs.length == 0) return null;
		if (animation == null) return null;
		var nameInxArr:Array<Int> = childsIndex.get(animation.name);
		if (nameInxArr != null) {
			var removeInx:Int = -1;
			for (inx in 0...nameInxArr.length) {
				var _inx:Int = nameInxArr[inx];
				if (childs[_inx] == animation) {
					removeInx = _inx;
					nameInxArr.splice(inx, 1);
					break;
				}
			}
			if (removeInx!=-1&&removeInx <= curInteratorInx) curInteratorInx -= 1;
			untyped animation.set_rander(null);
			var _child = childs.splice(removeInx, 1)[0];
			if (childs.length == 0) {
				childsIndex = new Hash<Array<Int>>();
				curInteratorInx = -1;
				curInteratorChild = null;
				this.swapIndexs = [];
			}
			if (_blitMode==false) {
				layer.removeChild(_child.sprite);
			}
			return _child;
		}
		return null;
	}
	
	public function removeChildAt(inx:Int):Animation
	{
		if (inx >= this.childs.length) return null;
		if (_blitMode==false) {
			layer.removeChildAt(inx);
		}
		return removeChild(this.childs[inx]);
	}
	public function getChildByName(name:String):Animation
	{
		if (childs.length == 0) return null;
		var nameInxArr:Array<Int> = childsIndex.get(name);
		if ( nameInxArr != null) {
			return this.childs[nameInxArr[0]];
		}
		return null;
	}
	public function removeChildByName(name:String):Animation
	{
		if (childs.length == 0) return null;
		var child:Animation = this.getChildByName(name);
		if (_blitMode==false) {
			layer.removeChild(child.sprite);
		}
		return this.removeChild(child);
	}
	public function getChildIndex(animation:Animation):Int
	{
		if (animation == null) return -1;
		if (childs.length == 0) return -1;
		var nameInxArr:Array<Int> = childsIndex.get(animation.name);
		if (nameInxArr != null) {
			for (inx in nameInxArr) {
				if (this.childs[inx] == animation) return inx;
			}
			return -1;
		}
		return -1;
	}
	public function removeAllChild():Void {
		childs.splice(0,childs.length);
		childsIndex = new Hash<Array<Int>>();
		curInteratorInx = -1;
		curInteratorChild = null;
		this.swapIndexs = [];
		if (_blitMode == false) {
			while (layer.numChildren > 0) layer.removeChildAt(0);
		}
	}
	public function swapChildren(child1:Animation, child2:Animation):Void
	{
		if (childs.length == 0) return;
		if (child1 == null && child2 == null) return;
		var rander1:Rander = child1.rander;
		if (rander1 == null) return;
		var rander2:Rander = child2.rander;
		if (rander2 == null) return;
		if (rander1 != rander2) return;
		var index1:Int = this.getChildIndex(child1);
		if (index1 == -1) return;
		var index2:Int = this.getChildIndex(child2);
		if (index2 == -1) return;
		this.swapChildrenAt(index1, index2);
	}
	public function swapChildrenAt(index1:Int, index2:Int):Void
	{
		if (childs.length == 0) return;
		if (index1 == index2) return;
		if (index1 >= childs.length || index2 >= childs.length) return;
		
		if (curInteratorInx == -1) {
			var child1:Animation = this.childs[index1];
			var child2:Animation = this.childs[index2];
			if (this._blitMode == false) {
				layer.swapChildren(child1.sprite, child2.sprite);
			}
			this.swapChildren(child1, child2);
		}else if (index1>curInteratorInx&&index2>curInteratorInx) {
			var child1:Animation = this.childs[index1];
			var child2:Animation = this.childs[index2];
			if (this._blitMode == false) {
				layer.swapChildren(child1.sprite, child2.sprite);
			}
			this.swapChildren(child1, child2);
		}else {
			swapIndexs.push([index1,index2]);
		}
	}
	public function rander(deltaTime:Int):Void 
	{
		if (layer == null) return;
		if (this.childs.length > 0) {
			while (true) {
				var _ani:Animation = getInteratorNext();
				if (_ani != null) {
					if (_ani.onUpdate != null) _ani.onUpdate();
				}else {
					curInteratorInx = -1;
					curInteratorChild = null;
					break;
				}
			}
			if (swapIndexs.length > 0) {
				for (swapArr in swapIndexs) {
					this.swapChildrenAt(swapArr[0], swapArr[1]);
				}
				swapIndexs = [];
			}
		}
		if (_blitMode == false) {
			for (ani in childs) {
				if (ani != null && ani.visible && ani.alpha > 0 && ani.scaleX > 0 && ani.scaleY > 0 && untyped ani.behaviorComplete == false) {
					untyped ani.update(deltaTime);
					untyped ani.updateed();
				}
			}
			return;
		}
		
		#if (flash||js)
		#if flash buffer.lock(); #end
		buffer.fillRect(clearRect, 0);
		#else
		tileDatas = new Array();
		layer.graphics.clear();
		#end
		for (ani in childs) {
			if (ani != null && ani.visible && ani.alpha > 0 && ani.scaleX > 0 && ani.scaleY > 0 && untyped ani.behaviorComplete == false) {
				untyped ani.update(deltaTime);
				frame = ani.curFrame;
				untyped frameBitmapData = Spritesheet.getBitmapData(ani.spritesheet, frame);
				//frameBitmapData.lock();
				behavior = ani.curBehavior;
				
				offx = (frame.offsetX - behavior.originX);
				offy = (frame.offsetY - behavior.originY);
				#if (flash || js)
				if (ani.scaleX != 1 || ani.scaleY != 1||ani.alpha != 1|| ani.color!=0xFFFFFF|| ani.rotation != 0)
				{
					
					matrix.b = matrix.c = 0;
					matrix.a = ani.scaleX;
					matrix.d = ani.scaleY;
					matrix.rotate(ani.angle);
					offx = offx*ani.scaleX;
					offy = offy*ani.scaleY;
					angle = Math.atan2(offy , offx)+ani.angle;
					dis = Math.sqrt(offx * offx + offy * offy);
					offy=Math.sin(angle)*dis;
					offx = Math.cos(angle) * dis;
					
					matrix.tx =ani.x+ offx;
					matrix.ty =ani.y+offy;
					if (ani.color != 0xFFFFFF || ani.alpha != 1) {
						untyped colorTransform.redMultiplier = ani._red;
						untyped colorTransform.blueMultiplier = ani._blue;
						untyped colorTransform.greenMultiplier = ani._green;
						untyped colorTransform.alphaMultiplier = ani.alpha;
						
						untyped buffer.draw(frameBitmapData, matrix, colorTransform, null, null, true);
					}else {
						untyped buffer.draw(frameBitmapData, matrix, null, null, null, true);
						
					}			
				}
				else
				{
					point.x = ani.x+offx;
					point.y = ani.y + offy;
					untyped buffer.copyPixels(frameBitmapData, frameBitmapData.rect, point, null, null, true);
				}
				//frameBitmapData.unlock();
				#else
				if (ani.rotation == 0) {
					if (ani.scaleX != 1 || ani.scaleY != 1) {
						offx = offx * ani.scaleX;
						offy = offy * ani.scaleY;
						angle = Math.atan2(offy , offx) +ani.angle;
						dis = Math.sqrt(offx * offx + offy * offy);
						offy=Math.sin(angle)*dis;
						offx = Math.cos(angle) * dis;
					}
					tileDatas.push(ani.x+offx);
					tileDatas.push(ani.y+offy);
					tileDatas.push(frame.tileid);
					tileDatas.push(ani.scaleX);
					tileDatas.push(0);
					tileDatas.push(0);
					tileDatas.push(ani.scaleY);
				}else {
					matrix.b = matrix.c = 0;
					matrix.a = ani.scaleX;
					matrix.d = ani.scaleY;
					matrix.rotate(-ani.angle); 
					
					if(ani.scaleX!=1)offx = offx*ani.scaleX;
					if(ani.scaleY!=1)offy = offy*ani.scaleY;
					angle = Math.atan2(offy , offx) +ani.angle;
					dis = Math.sqrt(offx * offx + offy * offy);
					offy=Math.sin(angle)*dis;
					offx = Math.cos(angle) * dis;
					
					tileDatas.push(ani.x+offx);
					tileDatas.push(ani.y +offy);
					tileDatas.push(frame.tileid);
					tileDatas.push(matrix.a);
					tileDatas.push(matrix.b);
					tileDatas.push(matrix.c);
					tileDatas.push(matrix.d);
				}
				untyped tileDatas.push(ani._red);
				untyped tileDatas.push(ani._green);
				untyped tileDatas.push(ani._blue);
				tileDatas.push(ani.alpha);
				#end
				untyped ani.updateed();
			}
		}
		#if (flash||js)
		#if flash buffer.unlock(); #end
		#else
		tilesheet.drawTiles(layer.graphics, tileDatas, true,Tilesheet.TILE_TRANS_2x2|Tilesheet.TILE_RGB|Tilesheet.TILE_ALPHA);
		#end
	}
	
	public function destory():Void {
		//layer = null;
		//#if (cpp || neko)
			//tilesheet = null;
		//#else
		//
		//#end
	}
}